﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Runtime.Serialization;

using SynapseGaming.LightingSystem.Core;
using SynapseGaming.LightingSystem.Editor;
using SynapseGaming.LightingSystem.Lights;
using SynapseGaming.LightingSystem.Rendering;

using Jitter;
using Jitter.Collision;
using Jitter.Collision.Shapes;
using Jitter.Dynamics;
using Jitter.Forces;
using Jitter.LinearMath;

namespace SunburnJitter
{
    /// <summary>
    /// Base Component for all other physical shape components
    /// </summary>
    public class RigidBodyComponent : BaseComponent<ISceneEntity>
    {
        private RigidBody _rigidBody;
        /// <summary>
        /// Gets or sets the rigid body, if it's set then the rigid body is added to the collision manager, so don't set it twice!!!
        /// </summary>
        public RigidBody RigidBody { get { return _rigidBody; } set { _rigidBody = value; sceneInterface.GetManager<JitterCollisionManager>(true).World.AddBody(_rigidBody); _rigidBody.Tag = this; } }

        /// <summary>
        /// Should this Rigid Body be drawn via the collision manager?
        /// </summary>
        public bool DebugDraw { get; set; }


        private Vector3 _localPosition;
        /// <summary>
        /// The local position of the shape
        /// </summary>
        public Vector3 LocalPosition { get { return _localPosition; } protected set { _localPosition = value; } } 

        private Vector3 _localOrientation;
        /// <summary>
        /// The local orientation of the shape
        /// </summary>
        public Vector3 LocalOrientation { get { return _localOrientation; } protected set { _localOrientation = value; } }

        //ToDo: Add more properties in order to use them in editor...

        //Transformations:
        protected Vector3 scale;
        protected Vector3 startPosition;
        protected Quaternion startOrientation;

        protected SceneInterface sceneInterface;

        public RigidBodyComponent()
        {
            this.sceneInterface = JitterCollisionManager.Instance.SceneInterface;
        }

        /// <summary>
        /// Tries to remove the rigid body from the scene
        /// </summary>
        public void Remove() {
            if (RigidBody != null && JitterCollisionManager.Instance != null)
                JitterCollisionManager.Instance.World.RemoveBody(RigidBody); 
        }

        public override void OnInitialize()
        {
            ParentObject.World.Decompose(out scale, out startOrientation, out startPosition);
            
            base.OnInitialize();
        }
        
        /// <summary>
        /// Tries to update the world matrix if rigid body is set.
        /// </summary>
        public override void OnUpdate(GameTime gametime)
        {
            if (RigidBody != null)
            {
                //construct world matrix:
                ParentObject.World = Matrix.CreateScale(scale) *
                    Matrix.CreateRotationX(-_localOrientation.X) * Matrix.CreateRotationY(-_localOrientation.Y) * Matrix.CreateRotationZ(-_localOrientation.Z) *   //local orientation
                    RigidBody.Orientation.ToXNAMatrix() * 
                    Matrix.CreateTranslation(RigidBody.Position.ToVector3() - Vector3.Transform(LocalPosition, RigidBody.Orientation.ToXNAMatrix()));
            }

            base.OnUpdate(gametime);
        }

        public override void SetObjectData(SerializationInfo info, StreamingContext context)
        {
            SerializationHelper.SerializeFieldOrEnum(ref _localPosition, info, "LocalPos");
            SerializationHelper.SerializeFieldOrEnum(ref _localOrientation, info, "LocalOrientation");
            base.SetObjectData(info, context);
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            SerializationHelper.DeserializeField(ref _localPosition, info, "LocalPos", true);
            SerializationHelper.DeserializeField(ref _localOrientation, info, "LocalOrientation", true);
            base.GetObjectData(info, context);
        }
    }
}
